package com.xncoding.freemarker.utile;

import com.jcraft.jsch.*;
import org.apache.commons.lang.StringUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

/**
 * sftp工具
 * User: huangyao
 * Date: 2020年5月19日
 * Time: 13点19分
 */
public class SFtpUtil {
    // 默认登陆超时时间
    public static final int CONNECT_TIMEOUT = 60000;
    // 创建通信通道超时时间
    public static final int CHANNEL_TIMEOUT = 60000;
    // 通信通道类型
    public static final String CHANNEL_TYPE = "sftp";
    //私钥路径-绝对路径
    public static final String FLAG_PRIVATEKEYPATH_ABSOLUTE = "1";
    //私钥路径-相对路径
    public static final String FLAG_PRIVATEKEYPATH_RELATIVE = "0";

    public static boolean uploadFile(SftpVo sftpVo) throws Exception {
        System.out.println("Start upload!");
        Session session = null;
        Channel channel = null;
        InputStream input = null;
        try {
            // 连接Sftp
            session = connectSftp(sftpVo);
            // 创建sftp通信通道
            channel = session.openChannel(CHANNEL_TYPE);
            channel.connect(CHANNEL_TIMEOUT);
            ChannelSftp sftp = (ChannelSftp) channel;
            createDir(sftpVo.getUploadSftpDirName(), sftp);
            sftp.cd(sftpVo.getUploadSftpDirName());
            File file = new File(sftpVo.getUploadDirName() + sftpVo.getFileName());
            input = new FileInputStream(file);
            sftp.put(input, sftpVo.getFileName());
            System.out.println("End upload!");
            return true;
        } catch (Exception e) {
            e.printStackTrace();
//            return false;
            throw e;
        } finally {
            closeStream(input, null);
            close(channel, session);
        }
    }

    /**
     * 利用JSch包实现SFTP下载文件
     */
    public static void sftpDownload(SftpVo sftpVo) throws Exception {
        File fileDir = new File(sftpVo.getDownloadDirName());
        fileDir.mkdirs();
        File downFile = new File(fileDir, sftpVo.getFileName());
//        if (downFile.exists()) return;

        Session session = null;
        Channel channel = null;
        InputStream input = null;
        OutputStream output = null;
        try {
            // 连接Sftp
            session = connectSftp(sftpVo);

            // 创建sftp通信通道
            channel = session.openChannel(CHANNEL_TYPE);
            channel.connect(CHANNEL_TIMEOUT);
            ChannelSftp sftp = (ChannelSftp) channel;

            if (StringUtils.isNotBlank(sftpVo.getSftpDir())) {
                // 进入服务器指定的文件夹
                sftp.cd(sftpVo.getSftpDir());
            }

            /*		//列出服务器指定的文件列表
                Vector v = sftp.ls("*.txt");
    			for(int i=0;i<v.size();i++){
    				System.out.println(v.get(i));
    			}*/

            // 以下代码实现从本地上传一个文件到服务器，如果要实现下载，对换以下流就可以了
    			/*OutputStream outstream = sftp.put(fileName);
    			InputStream instream = new FileInputStream(new File("c:/print.txt"));*/
            input = sftp.get(sftpVo.getFileName());

            output = new FileOutputStream(downFile);

            byte b[] = new byte[1024];
            int n;
            while ((n = input.read(b)) != -1) {
                output.write(b, 0, n);
            }

            output.flush();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            closeStream(input, output);
            close(channel, session);
        }
    }

    /**
     * 利用JSch包实现SFTP下载、上传文件，新的文件名
     */
    public static void sftpDownload(SftpVo sftpVo, String newName) throws Exception {
        File fileDir = new File(sftpVo.getDownloadDirName());
        fileDir.mkdirs();
        File downFile = new File(fileDir, newName);
//        if (downFile.exists()) return;

        Session session = null;
        Channel channel = null;
        InputStream input = null;
        OutputStream output = null;
        try {
            // 连接Sftp
            session = connectSftp(sftpVo);

            // 创建sftp通信通道
            channel = session.openChannel(CHANNEL_TYPE);
            channel.connect(CHANNEL_TIMEOUT);
            ChannelSftp sftp = (ChannelSftp) channel;

            if (StringUtils.isNotBlank(sftpVo.getSftpDir())) {
                // 进入服务器指定的文件夹
                sftp.cd(sftpVo.getSftpDir());
            }

            input = sftp.get(sftpVo.getFileName());

            output = new FileOutputStream(downFile);

            byte b[] = new byte[1024];
            int n;
            while ((n = input.read(b)) != -1) {
                output.write(b, 0, n);
            }

            output.flush();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            closeStream(input, output);
            close(channel, session);
        }
    }

    /**
     * 连接Sftp
     *
     * @param sftpVo
     * @return
     */
    public static Session connectSftp(SftpVo sftpVo) throws Exception {
        if (StringUtils.isNotBlank(sftpVo.getPassword())) {
            // 使用密码方式登录
            return connectSftpWithPassword(sftpVo);
        } else {
            // 使用免密码登录(RSA)
            return connectSftpWithRSA(sftpVo);
        }
    }

    /**
     * 连接Sftp（使用密码方式登录）
     *
     * @param sftpVo
     * @return
     */
    public static Session connectSftpWithPassword(SftpVo sftpVo) throws Exception {
        Session session = null;

        try {
            JSch jsch = new JSch();
            if (sftpVo.getPort() <= 0) {
                // 连接服务器，采用默认端口
                session = jsch.getSession(sftpVo.getUser(), sftpVo.getIp());
            } else {
                // 采用指定的端口连接服务器
                session = jsch.getSession(sftpVo.getUser(), sftpVo.getIp(), sftpVo.getPort());
            }
            // 如果服务器连接不上，则抛出异常
            if (session == null) {
                throw new Exception("session is null");
            }

            // 设置登陆主机的密码
            session.setPassword(sftpVo.getPassword());//设置密码
            // 设置第一次登陆的时候提示，可选值：(ask | yes | no)
            session.setConfig("StrictHostKeyChecking", "no");
            // 设置登陆超时时间
            session.connect(CONNECT_TIMEOUT);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        return session;
    }

    /**
     * 连接Sftp（使用代理）
     *
     * @param sftpVo
     * @return
     */
    public static Session connectSftpWithProxy(SftpVo sftpVo) throws Exception {
        Session session = null;

        try {
            JSch jsch = new JSch();
            if (sftpVo.getPort() <= 0) {
                // 连接服务器，采用默认端口
                session = jsch.getSession(sftpVo.getUser(), sftpVo.getIp());
            } else {
                // 采用指定的端口连接服务器
                session = jsch.getSession(sftpVo.getUser(), sftpVo.getIp(), sftpVo.getPort());
            }
//            session.setProxy(new ProxyHTTP(sftpVo.getProxyIp(),sftpVo.getProxyPort()));
            // 如果服务器连接不上，则抛出异常
            if (session == null) {
                throw new Exception("session is null");
            }
            // 设置登陆主机的密码
            session.setPassword(sftpVo.getPassword());//设置密码
            // 设置第一次登陆的时候提示，可选值：(ask | yes | no)
            session.setConfig("StrictHostKeyChecking", "no");
            // 设置登陆超时时间
            session.connect(CONNECT_TIMEOUT);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        return session;
    }

    /**
     * 连接Sftp（使用免密码登录）
     *
     * @param sftpVo
     * @return
     */
    public static Session connectSftpWithRSA(SftpVo sftpVo) throws Exception {
        Session session = null;

        JSch jsch = new JSch();
//        URL url = ClassLoader.getSystemResource(sftpVo.getPrivateKeyPath());
//        logger.info("path:"+ClassLoader.getSystemResource("/").getPath());
        if (sftpVo.getFlagPrivateKeyPath() != null && FLAG_PRIVATEKEYPATH_ABSOLUTE.equals(sftpVo.getFlagPrivateKeyPath())) {
            System.out.println("absolute path key:" + sftpVo.getPrivateKeyPath() + ",url:" + sftpVo.getPrivateKeyPath());
            jsch.addIdentity(sftpVo.getPrivateKeyPath(), sftpVo.getPassphrase());
        } else {
            System.out.println("relative path key:" + sftpVo.getPrivateKeyPath() + ",url:" + SFtpUtil.class.getClassLoader().getResource(sftpVo.getPrivateKeyPath()));
            jsch.addIdentity(SFtpUtil.class.getClassLoader().getResource(sftpVo.getPrivateKeyPath()).getPath(), sftpVo.getPassphrase());
        }


        if (sftpVo.getPort() <= 0) {
            //连接服务器，采用默认端口
            session = jsch.getSession(sftpVo.getUser(), sftpVo.getIp());
        } else {
            //采用指定的端口连接服务器
            session = jsch.getSession(sftpVo.getUser(), sftpVo.getIp(), sftpVo.getPort());
        }

        //如果服务器连接不上，则抛出异常
        if (session == null) {
            throw new Exception("session is null");
        }

        //设置第一次登陆的时候提示，可选值：(ask | yes | no)
        session.setConfig("StrictHostKeyChecking", "no");
        //设置登陆超时时间
        session.connect(CONNECT_TIMEOUT);
        return session;
    }

    /**
     * 关闭sftp通信通道，关闭与sftp连接
     *
     * @param channel
     * @param session
     */
    public static void close(Channel channel, Session session) {
        if (channel != null) {
            channel.disconnect();
        }
        if (session != null) {
            session.disconnect();
        }
    }

    /**
     * 关闭输入输出流
     *
     * @param input
     * @param output
     */
    private static void closeStream(InputStream input, OutputStream output) {
        if (output != null) {
            try {
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (input != null) {
            try {
                input.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 判断文件是否存在
     *
     * @param sftpVo sftpDir 文件夹
     *               downloadFileName 文件名
     * @return
     * @throws Exception
     */
    public static boolean isFileExist(SftpVo sftpVo) throws Exception {
        Session session = null;
        Channel channel = null;
        try {
            session = connectSftp(sftpVo);
            //创建sftp通信通道
            channel = session.openChannel(CHANNEL_TYPE);
            channel.connect(SFtpUtil.CHANNEL_TIMEOUT);
            ChannelSftp sftp = (ChannelSftp) channel;

            return isFileExist(sftpVo.getSftpDir() + "/" + sftpVo.getFileName(), sftp);
        } finally {
            close(channel, session);
        }
    }

    /**
     * 判断文件是否存在
     * <p>Description:</p>
     *
     * @param srcSftpFilePath
     * @return
     * @throws SftpException
     * @Title: isFileExist
     * @author suxx
     */
    public static boolean isFileExist(String srcSftpFilePath, ChannelSftp sftp) throws SftpException {
        boolean isExitFlag = false;
        // 文件大于等于0则存在文件
        if (getFileSize(srcSftpFilePath, sftp) >= 0) {
            isExitFlag = true;
        }
        return isExitFlag;
    }

    /**
     * 获得文件大小
     * <p>Description:</p>
     *
     * @param srcSftpFilePath
     * @return
     * @throws SftpException
     * @Title: getFileSize
     * @author suxx
     */
    public static long getFileSize(String srcSftpFilePath, ChannelSftp sftp) throws SftpException {
        long filesize = 0;//文件大于等于0则存在
        try {
            SftpATTRS sftpATTRS = sftp.lstat(srcSftpFilePath);
            filesize = sftpATTRS.getSize();
        } catch (Exception e) {
            e.printStackTrace();
            filesize = -1;//获取文件大小异常
            if (e.getMessage().toLowerCase().equals("no such file")) {
                filesize = -2;//文件不存在
            }
        }
        return filesize;
    }

    /**
     * 获取文件列表
     *
     * @param sftpVo
     * @return
     * @throws Exception
     */
    public static List<String> getFileList(SftpVo sftpVo, String encode) throws Exception {
        Session session = null;
        Channel channel = null;

        try {
            // 连接Sftp
            session = connectSftp(sftpVo);

            // 创建sftp通信通道
            channel = session.openChannel(CHANNEL_TYPE);
            channel.connect(CHANNEL_TIMEOUT);
            ChannelSftp sftp = (ChannelSftp) channel;
            // 进入服务器指定的文件夹
            sftp.cd(sftpVo.getSftpDir());

            Vector<ChannelSftp.LsEntry> vector = sftp.ls(sftpVo.getSftpDir());
            sftp.setTerminalMode("binary".getBytes());
            //sftp.setFilenameEncoding("utf-8");

            List<String> result = new ArrayList();
            String fileName = "";
            for (ChannelSftp.LsEntry lsEntry : vector) {
                fileName = new String(lsEntry.getFilename().getBytes("ISO-8859-1"), encode);
                if (".".equals(fileName) || "..".equals(fileName)){
                    continue;
                }else {
                    result.add(fileName);
                }
            }
            return result;

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            close(channel, session);
        }
    }

    /**
     * 获取文件数量
     *
     * @return
     */
    public static int getFileCount(SftpVo sftpVo) throws Exception {
        Session session = null;
        Channel channel = null;
        try {
            // 连接Sftp
            session = connectSftp(sftpVo);

            // 创建sftp通信通道
            channel = session.openChannel(CHANNEL_TYPE);
            channel.connect(CHANNEL_TIMEOUT);
            ChannelSftp sftp = (ChannelSftp) channel;

            if (StringUtils.isNotBlank(sftpVo.getSftpDir())) {
                // 进入服务器指定的文件夹
                sftp.cd(sftpVo.getSftpDir());
            }

            Vector vector = sftp.ls(sftpVo.getFileName());

            return vector.size();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            close(channel, session);
        }
    }

    /**
     * 创建一个文件目录
     */
    public static void createDir(String createpath, ChannelSftp sftp) {
        try {
            if (isDirExist(createpath, sftp)) {
                sftp.cd(createpath);
                return;
            }
            String pathArry[] = createpath.split("/");
            StringBuffer filePath = new StringBuffer("/");
            for (String path : pathArry) {
                if (path.equals("")) {
                    continue;
                }
                filePath.append(path + "/");
                if (isDirExist(filePath.toString(), sftp)) {
                    sftp.cd(filePath.toString());
                } else {
                    // 建立目录
                    sftp.mkdir(filePath.toString());
                    // 进入并设置为当前目录
                    sftp.cd(filePath.toString());
                }
            }
            sftp.cd(createpath);
        } catch (SftpException e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断目录是否存在
     */
    public static boolean isDirExist(String directory, ChannelSftp sftp) {
        boolean isDirExistFlag = false;
        try {
            SftpATTRS sftpATTRS = sftp.lstat(directory);
            isDirExistFlag = true;
            return sftpATTRS.isDir();
        } catch (Exception e) {
            if (e.getMessage().toLowerCase().equals("no such file")) {
                isDirExistFlag = false;
            }
            e.printStackTrace();
        }
        return isDirExistFlag;
    }

    private static File getFileDir(String dataDirName) {
        String path = SFtpUtil.class.getClassLoader().getResource("").getPath();
        File f = new File(path);
        String pf = f.getParent() + "/" + dataDirName;

        return new File(pf);
    }

}